import numpy as np
import pandas as pd
# sklearn
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_moons, make_circles, make_classification, load_digits
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.preprocessing import scale
# Tensorflow
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# Visualisation libraries
## Text
from colorama import Fore, Back, Style
from IPython.display import Image, display, Markdown, Latex, clear_output
## matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
## plotly
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
import plotly.offline as py
from plotly.subplots import make_subplots
import plotly.express as px
## seaborn
import seaborn as sns
sns.set_context('paper', rc={'font.size':12,'axes.titlesize':14,'axes.labelsize':12})
sns.set_style('white')
plt.style.use('seaborn-whitegrid')
import warnings
warnings.filterwarnings("ignore")
In this article, we go through basic image classification using Multi-layer Perceptron (MLP). For testing the algorithm, we use sklearn digit dataset.
fig, ax = plt.subplots(1, 10, figsize=(17, 6))
ax = ax.ravel()
digits = load_digits()
for ax, (image, label) in zip(ax, list(zip(digits.images, digits.target))):
ax.set_axis_off()
ax.imshow(image, cmap= 'Greys', interpolation='nearest')
ax.set_title('%i' % label, fontsize = 10)
X = digits.images
X = X / 256.0
y = digits.target
Labels = np.unique(y)
One of the efficient methods of splitting a dataset into random train and test subsets is using sklearn.model_selection.train_test_split.
y = keras.utils.to_categorical(y, num_classes=len(Labels))
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
pd.DataFrame(data={'Set':['X_train','X_test','y_train','y_test'],
'Shape':[X_train.shape, X_test.shape, y_train.shape, y_test.shape]}).set_index('Set').T
| Set | X_train | X_test | y_train | y_test |
|---|---|---|---|---|
| Shape | (1257, 8, 8) | (540, 8, 8) | (1257, 10) | (540, 10) |
The goal of this approach is to classify the images by focusing on the relationship between nearby pixels. A simple implementation of an image classifier can be performed in Keras using Multi-layer Perceptron (MLP) Image Classification as follows.
model = keras.Sequential(name = 'MLP_Image_Classification')
model.add(layers.Flatten(input_shape=X[0].shape, name='Layer1'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(32, activation='relu', name='Layer2'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(len(Labels), activation='softmax', name='Layer3'))
#
model.summary()
tf.keras.utils.plot_model(model, show_shapes=True, show_layer_names=True, expand_nested = True, rankdir = 'LR')
Model: "MLP_Image_Classification" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= Layer1 (Flatten) (None, 64) 0 _________________________________________________________________ dropout (Dropout) (None, 64) 0 _________________________________________________________________ Layer2 (Dense) (None, 32) 2080 _________________________________________________________________ dropout_1 (Dropout) (None, 32) 0 _________________________________________________________________ Layer3 (Dense) (None, 10) 330 ================================================================= Total params: 2,410 Trainable params: 2,410 Non-trainable params: 0 _________________________________________________________________
# Number of iterations
IT = int(5e2)+1
model.compile(optimizer= 'rmsprop', loss='binary_crossentropy', metrics=['accuracy','mae', 'mse'])
# Training model
history = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs= IT, verbose = 0)
def Search_List(Key, List): return [s for s in List if Key in s]
Metrics_Names = {'loss':'Loss', 'accuracy':'Accuracy', 'mae':'MAE', 'mse':'MSE'}
def Table_modify(df, Metrics_Names = Metrics_Names):
df = df.rename(columns = Metrics_Names)
df = df.reindex(sorted(df.columns), axis=1)
df.insert(loc = 0, column = 'Iteration', value = np.arange(0, df.shape[0]), allow_duplicates=False)
return df
Validation_Table = Search_List('val_',history.history.keys())
Train_Table = list(set( history.history.keys()) - set(Validation_Table))
Validation_Table = pd.DataFrame(np.array([history.history[x] for x in Validation_Table]).T, columns = Validation_Table)
Train_Table = pd.DataFrame(np.array([history.history[x] for x in Train_Table]).T, columns = Train_Table)
Validation_Table.columns = [x.replace('val_','') for x in Validation_Table.columns]
Train_Table = Table_modify(Train_Table)
Validation_Table = Table_modify(Validation_Table)
# Train Set Score
score = model.evaluate(X_test, y_test, batch_size=128, verbose = 0)
score = pd.DataFrame(score, index = model.metrics_names).T
score.index = ['Train Set Score']
# Validation Set Score
Temp = model.evaluate(X_train, y_train, batch_size=128, verbose = 0)
Temp = pd.DataFrame(Temp, index = model.metrics_names).T
Temp.index = ['Validation Set Score']
score = score.append(Temp)
score.rename(columns= Metrics_Names, inplace = True)
score = score.reindex(sorted(score.columns), axis=1)
display(score.style.set_precision(4))
| Accuracy | Loss | MAE | MSE | |
|---|---|---|---|---|
| Train Set Score | 0.9093 | 0.0716 | 0.0548 | 0.0184 |
| Validation Set Score | 0.9364 | 0.0697 | 0.0543 | 0.0174 |
Let's define some function by which we can analyze the performance of the modeling.
def Plot_history(history, yLim = 2, Title = False, Table_Rows = 25):
fig = make_subplots(rows=1, cols=2, horizontal_spacing = 0.02, column_widths=[0.6, 0.4],
specs=[[{"type": "scatter"},{"type": "table"}]])
# Left
fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['Loss'].values,
line=dict(color='OrangeRed', width= 1.5), name = 'Loss'), 1, 1)
fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['Accuracy'].values,
line=dict(color='MidnightBlue', width= 1.5), name = 'Accuracy'), 1, 1)
fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['MAE'].values,
line=dict(color='ForestGreen', width= 1.5), name = 'Mean Absolute Error (MAE)'), 1, 1)
fig.add_trace(go.Scatter(x= history['Iteration'].values, y= history['MSE'].values,
line=dict(color='purple', width= 1.5), name = 'Mean Squared Error (MSE)'), 1, 1)
fig.update_layout(legend=dict(x=0, y=1.1, traceorder='reversed', font_size=12),
dragmode='select', plot_bgcolor= 'white', height=600, hovermode='closest',
legend_orientation='h')
fig.update_xaxes(range=[history.Iteration.min(), history.Iteration.max()],
showgrid=True, gridwidth=1, gridcolor='Lightgray',
showline=True, linewidth=1, linecolor='Lightgray', mirror=True, row=1, col=1)
fig.update_yaxes(range=[0, yLim], showgrid=True, gridwidth=1, gridcolor='Lightgray',
showline=True, linewidth=1, linecolor='Lightgray', mirror=True, row=1, col=1)
# Right
ind = np.linspace(0, history.shape[0], Table_Rows, endpoint = False).round(0).astype(int)
ind = np.append(ind, history.Iteration.values[-1])
history = history[history.index.isin(ind)]
Temp = []
for i in history.columns:
Temp.append(history.loc[:,i].astype(float).round(4).values)
fig.add_trace(go.Table(header=dict(values = list(history.columns), line_color='darkslategray',
fill_color='DimGray', align=['center','center'],
font=dict(color='white', size=12), height=25), columnwidth = [0.4, 0.4, 0.4, 0.4],
cells=dict(values=Temp, line_color='darkslategray', fill=dict(color=['WhiteSmoke', 'white']),
align=['center', 'center'], font_size=12,height=20)), 1, 2)
if Title != False:
fig.update_layout(plot_bgcolor= 'white',
title={'text': Title, 'x':0.46, 'y':0.94, 'xanchor': 'center', 'yanchor': 'top'},
yaxis_title='Frequency')
fig.show()
def Confusion_Matrix(Model, X, y, Labels, FG = (14, 5)):
fig, ax = plt.subplots(1, 2, figsize=FG)
y_pred = Model.predict(X)
if y.shape[1] > 1:
CM = confusion_matrix(y.argmax(axis = 1), y_pred.argmax(axis = 1))
else:
CM = confusion_matrix(y, np.round(y_pred))
_ = sns.heatmap(CM.round(2), annot=True, annot_kws={"size": 14}, cmap="Blues", ax = ax[0])
_ = ax[0].set_xlabel('Predicted labels')
_ = ax[0].set_ylabel('True labels');
_ = ax[0].set_title('Confusion Matrix');
_ = ax[0].xaxis.set_ticklabels(Labels)
_ = ax[0].yaxis.set_ticklabels(Labels)
CM = CM.astype('float') / CM.sum(axis=1)[:, np.newaxis]
_ = sns.heatmap(CM.round(2), annot=True, annot_kws={"size": 14}, cmap="Greens", ax = ax[1],
linewidths = 0.2, vmin=0, vmax=1, cbar_kws={"shrink": 1})
_ = ax[1].set_xlabel('Predicted labels')
_ = ax[1].set_ylabel('True labels');
_ = ax[1].set_title('Normalized Confusion Matrix');
_ = ax[1].xaxis.set_ticklabels(Labels)
_ = ax[1].yaxis.set_ticklabels(Labels)
return fig, ax
Pred = model.predict(X_test)
def Pred_Plot(i, Pred = Pred, y_test = y_test, s = 1):
Pred_Labels = np.argmax(Pred, axis = 1)
True_Labels = np.argmax(y_test, axis =1)
Img = X_test[i]*256
Prop = Pred[i]
fig, ax = plt.subplots(1, 2, figsize=(13*s, 6*s))
# Left
_ = ax[0].imshow(Img, cmap=plt.cm.binary)
_ = ax[0].set_axis_off()
if Pred_Labels[i] == True_Labels[i]:
Color = 'blue'
else:
Color = 'red'
_ = fig.suptitle('Predicted Label = %i, True Label = %i, Accuracy = %% %.2f' %
(Pred_Labels[i], True_Labels[i], 100*Pred.max(axis = 1)[i]), color=Color)
# Right
_ = sns.barplot(ax = ax[1], x= Labels, y= Prop, palette='summer', edgecolor='k', hatch="///")
_ = ax[1].set_yscale('log')
_ = ax[1].set_ylim(top = 1)
Plot_history(Train_Table, yLim = 1, Title = 'Train Set')
Plot_history(Validation_Table, yLim = 1, Title = 'Validation Set')
The confusion matrix allows for visualization of the performance of an algorithm.
fig, _ = Confusion_Matrix(model, X_train, y_train, Labels, FG = (17, 6))
_ = fig.suptitle('Train Set', fontsize = 16)
fig, _ = Confusion_Matrix(model, X_test, y_test, Labels, FG = (17, 6))
_ = fig.suptitle('Test Set', fontsize = 16)
Pred_Labels = np.argmax(Pred, axis = 1)
True_Labels = np.argmax(y_test, axis =1)
# redicted Labels (Correct)
C = np.where(abs(Pred_Labels - True_Labels) == 0)[0]
# Predicted Labels (Wrong)
W = np.nonzero(abs(Pred_Labels - True_Labels))[0]
For example, we can randomly pick an entry from the correctly predicted labels list
Pred_Plot(np.random.choice(C), s = 3/4)
Similarly, from the wrongly predicted labels list
Pred_Plot(np.random.choice(W), s = 3/4)